# gozerbot/persist.py
#
#

""" 
    allow data to be written to disk in JSON format. creating the persisted 
    object restores data.

"""

__status__ = "seen"

## gozerbot imports

from gozerbot.stats import stats
from gozerbot.utils.locking import lockdec
from gozerbot.utils.log  import rlog
from gozerbot.utils.trace import calledfrom
from gozerbot.utils.lazydict import LazyDict
from gozerbot.datadir import datadir
from gozerbot.config import config
from simplejson import load, dump, loads, dumps

## basic imports

import pickle
import thread
import os
import copy
import sys
import types
import logging

## defines

saving = []
stopsave = 0

## locks

persistlock = thread.allocate_lock()
persistlocked = lockdec(persistlock)

## Persist class

class Persist(object):

    """ persist data attribute to JSON file. """

    def __init__(self, filename, default=None, init=True):
        self.fn = filename # filename to save to
        self.lock = thread.allocate_lock() # lock used when saving)
        self.data = LazyDict() # attribute to hold the data
        if init:
            if default == None:
                default = LazyDict()
            self.init(default)

    @persistlocked
    def init(self, default={}):
        """ initialize the data. """
        rlog(0, 'persist', 'reading %s' % self.fn)
        try: datafile = open(self.fn, 'r')
        except IOError, ex:
            if not 'No such file' in str(ex):
                rlog(10, 'persist', 'failed to read %s: %s' % (self.fn, str(ex)))
                self.data = copy.deepcopy(default)
                raise
            else: return
        try:
            self.data = load(datafile)
            datafile.close()
            stats.up('persist', 'load') 
            if type(self.data) == types.DictType:
                d = LazyDict()
                d.update(self.data)
                self.data = d
        except Exception, ex: rlog(100, 'persist', 'ERROR: %s' % self.fn) ; raise

    @persistlocked
    def save(self):
        """ persist data attribute. """
        if stopsave: rlog(100, 'persist', 'stopping mode .. not saving %s' % self.fn) ; return
        try:
            d = []
            for p in self.fn.split(os.sep)[:-1]:
                if not p: continue
                d.append(p)
                pp = os.sep.join(d)
                if not os.path.isdir(pp):
                    logging.warn("creating %s dir" % pp)
                    os.mkdir(pp)
            saving.append(str(self.fn))
            tmp = self.fn + '.tmp' # tmp file to save to
            try: datafile = open(tmp, 'w')
            except IOError, ex: rlog(100, 'persist', "can't save %s: %s" % (self.fn, str(ex))) ; return
            dump(self.data, datafile)
            datafile.close()
            try: os.rename(tmp, self.fn)
            except WindowsError:
                os.remove(self.fn)
                os.rename(tmp, self.fn)
            stats.up('persist', 'saved')
            rlog(10, 'persist', '%s saved' % self.fn)
        finally:
            try: saving.remove(self.fn)
            except ValueError: pass

## PlugPersist class

class PlugPersist(Persist):

    """ persist plug related data. """

    def __init__(self, filename, default=None):
        plugname = calledfrom(sys._getframe())
        Persist.__init__(self, datadir + os.sep + 'plugs' + os.sep + plugname + os.sep + filename, default)

## LazuDictPersist class

class LazyDictPersist(Persist):

    """ persisted lazy dict. """

    def __init__(self, default={}):
        Persist.__init__(self)
        if not self.data: self.data = LazyDict(default)
